package runner.PSO_VM;

import org.cloudbus.cloudsim.CloudletSchedulerSpaceShared;
import org.cloudbus.cloudsim.Host;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.power.PowerHost;
import org.cloudbus.cloudsim.power.PowerVmAllocationPolicyAbstract;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class PowerVmAllocationPolicyPSO extends PowerVmAllocationPolicyAbstract {

    private static double[] mapping = null;
    private static PSO PSOSchedularInstance;
    private List<PowerHost> hostList;
    private List<Vm> vmList;

    public PowerVmAllocationPolicyPSO(List<? extends Vm> vmList, List<? extends Host> hostList) {
        super(hostList);
        this.hostList = (List<PowerHost>) hostList;
        this.vmList = (List<Vm>) vmList;
        PSOSchedularInstance = new PSO(this.vmList, this.hostList);
        mapping = PSOSchedularInstance.run();
    }

    private static List<Vm> createVM(int userId, int vms) {
        //Creates a container to store VMs. This list is passed to the broker later
        LinkedList<Vm> list = new LinkedList<>();

        //VM Parameters
        // 对虚拟机的参数进行设置，主要包括ID、用户ID、MIPS、CPU数量、内存、带宽、外存、虚拟机监控器、调度策略。
        long size = 10000; //image size (MB)
        int ram = 512; //vm memory (MB)
        int mips = 800;
        long bw = 1000;
        int pesNumber = 1; //number of cpus
        String vmm = "Xen"; //VMM name

        //create VMs
        Vm[] vm = new Vm[vms];

        for (int i = 0; i < vms; i++) {
            vm[i] = new Vm(i, userId, mips, pesNumber, ram, bw, size, vmm, new CloudletSchedulerSpaceShared());
            list.add(vm[i]);
        }

        return list;
    }

    @Override
    public boolean allocateHostForVm(Vm vm, Host host) {
        if (host == null) {
            Log.formatLine("%.2f: No suitable host found for VM #" + vm.getId() + "\n", CloudSim.clock());
            return false;
        }
        if (host.vmCreate(vm)) { // if vm has been succesfully created in the host
            // put()方法将指定的键/值对插入到HashMap中。语法为：hashmap.put(K key，V value)
            getVmTable().put(vm.getUid(), host);
            Log.formatLine(
                    "%.2f: VM #" + vm.getId() + " has been allocated to the host #" + host.getId(),
                    CloudSim.clock());
            return true;
        }
        Log.formatLine(
                "%.2f: Creation of VM #" + vm.getId() + " on the host #" + host.getId() + " failed\n",
                CloudSim.clock());
        return false;
    }

    public PowerHost findHostForVm (Vm vm) {
        List<PowerHost> hostList = this.<PowerHost> getHostList();
        int vmId = vm.getId();
        PowerHost host = hostList.get((int) mapping[vmId]);
        if (host.isSuitableForVm(vm)) {
            return host;
        }else {
            for (PowerHost host1 : hostList) {
                if (host1.isSuitableForVm(vm)) {
                    return host1;
                }
            }
        }
        return null;
    }


    @Override
    public List<Map<String, Object>> optimizeAllocation(List<? extends Vm> vmList) {
        return null;
    }

    public double[][] getVmsRequestCapacity (List<? extends Vm> vmList) {
        double[][] request = new double[vmList.size()][3];
        for (int i=0; i<vmList.size(); i++) {
            Vm vm = vmList.get(i);
            request[i][0] = vm.getCurrentRequestedMaxMips();
            //request[i][1] = vm.getCurrentRequestedMaxMips();
            request[i][1] = vm.getCurrentRequestedRam();
            request[i][2] = vm.getCurrentRequestedBw();
        }
        return request;
    }

    public double[][] getHostsRemainCapacity (List<PowerHost> hostList) {
        double cpuT = 1.0;  // 0.8 cpu利用率阈值为0.8(判断是否过载)
        double ramT = 1.0;
        double bwT = 1.0;
        //List<PowerHost> hostList = getHostList();
       /*List<PowerHost> appreciateHosts = new ArrayList<PowerHost>();
       for(PowerHost h : hostList){
           if(excludedHosts.contains(h)){
               continue;
           }
           appreciateHosts.add(h);
       }
       double[][] remain = new double[appreciateHosts.size()][3];*/
        double[][] remain = new double[hostList.size()][4];
        int hostIndex = 0;
        for (PowerHost h : hostList) {
//            remain[hostIndex][0] = h.getAvailableMips() * cpuT - h.getUtilizationOfCpuMips();
            remain[hostIndex][0] = h.getVmScheduler().getPeCapacity() * cpuT - h.getUtilizationOfCpuMips();
            remain[hostIndex][1] = h.getVmScheduler().getPeCapacity() - h.getUtilizationOfCpuMips();
            remain[hostIndex][2] = h.getRam() * ramT - h.getUtilizationOfRam();
            remain[hostIndex][3] = h.getBw() * bwT - h.getUtilizationOfBw();
            hostIndex ++;
        }
        return remain;
    }



}
